package minijava;

import java.util.*;
import java.lang.Integer;
import minijava.node.*;
import minijava.analysis.*;
import minijava.LabeledGraph;

/* Analyse the AST looking for errors */
public class ASTAnalyser extends DepthFirstAdapter
{
    private boolean error;
    private Hashtable<String, Integer> classNames;
    private LabeledGraph<String> inheritanceGraph;

    ASTAnalyser(){
	error = false;
	classNames = new Hashtable<String, Integer>();
	inheritanceGraph = new LabeledGraph<String>();
    }

    public void error(String s){
	System.out.println("error: " + s);
	error = true;
    }

    public void validateClasses(){
	LinkedList<String> cycle = inheritanceGraph.findLabeledCycle();
	if(cycle != null){
	    String f = cycle.getFirst();
	    String e = new String();
	    for(String s : cycle){
		e = e.concat("'"+s+"' <- ");
	    }
	    e = e.concat("'"+f+"'");
	    error("circular inheritance: " + e);
	}
    }

    public void inAMainClass(AMainClass node)
    {
	String name = node.getClassName().getText();
	Integer v = classNames.get(name);
	if(v != null){
	    if(v.intValue()>=0)
		error("[" + node.getClassName().getLine() + "," + node.getClassName().getPos() + "] multiple declarations of class '" + name + "'");
	    else
		classNames.put(name, -v.intValue()-1);
	}else{
	    classNames.put(name, classNames.size());
	    inheritanceGraph.addVertex(name);
	}
    }

    public void inASimpleClassDecl(ASimpleClassDecl node)
    {
	String name = node.getClassName().getText();
	Integer v = classNames.get(name);
	if(v != null){
	    if(v.intValue()>=0)
		error("[" + node.getClassName().getLine() + "," + node.getClassName().getPos() + "] multiple declarations of class '" + name + "'");
	    else
		classNames.put(name, -v.intValue()-1);
	}else{
	    classNames.put(name, classNames.size());
	    inheritanceGraph.addVertex(name);
	}
    }

    public void inAExtendsClassDecl(AExtendsClassDecl node){
	String name1 = node.getClassName().getText();
	Integer v1 = classNames.get(name1);
	if(v1 != null){
	    if(v1.intValue()>=0){
		error("[" + node.getClassName().getLine() + "," + node.getClassName().getPos() + "] multiple declarations of class '" + name1 + "'");
		return;
	    }else{
		classNames.put(name1, -v1.intValue()-1);
		v1 = new Integer(-v1.intValue()-1);
	    }
	}else{
	    v1 = new Integer(classNames.size());
	    classNames.put(name1, classNames.size());
	    inheritanceGraph.addVertex(name1);
	}
	String name2 = node.getParentName().getText();
	if(name2.equals(name1)){
	    error("[" + node.getClassName().getLine() + "," + node.getClassName().getPos() + "] class '" + name1 + "' inherits from itself");
	    return;
	}
	Integer v2 = classNames.get(name2);
	if(v2 != null){
	    if(v2.intValue() < 0)
		v2 = new Integer(-v2.intValue()-1);
	    inheritanceGraph.addEdge(v1, v2);
	}else{
	    v2 = new Integer(classNames.size());
	    classNames.put(name2, -v2.intValue()-1);
	    inheritanceGraph.addVertex(name2);
	    inheritanceGraph.addEdge(v1, v2);
	}
    }

    public void inAMethodDecl(AMethodDecl node) {

        LinkedList<PVarDecl> copy = node.getArgs();
	if(copy.size()>0){
	    for(int i = 0; i<copy.size(); i++){
		for(int j = i+1; j<copy.size(); j++){
		    String firstParameter = copy.get(i).toString();
		    String secondParameter = copy.get(j).toString();
		    if(firstParameter.equals(secondParameter)){
			error("[" + node.getId().getLine() + "," + node.getId().getPos() + "] parameters with repeated names '" + firstParameter.substring(0, firstParameter.length()-1) + "'");
		    }
		}
	    }
	}
    }
}
